package com.imike.akka;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.ujmp.core.DenseMatrix;
import org.ujmp.core.Matrix;
import org.ujmp.core.calculation.Calculation;
import org.ujmp.core.doublematrix.DenseDoubleMatrix2D;

public class KNN {

    private static LinkedHashMap<Integer, Double> sortMapByValue(Map<Integer, Double> oriMap) {
        LinkedHashMap<Integer, Double> sortedMap = new LinkedHashMap<Integer, Double>();
        if (oriMap != null && !oriMap.isEmpty()) {
            List<Map.Entry<Integer, Double>> entryList = new ArrayList<Map.Entry<Integer, Double>>(oriMap.entrySet());
            Collections.sort(entryList, new Comparator<Map.Entry<Integer, Double>>() {
                public int compare(Entry<Integer, Double> entry1, Entry<Integer, Double> entry2) {
                    
                    double value1 = entry1.getValue();
                    double value2 = entry2.getValue();

                    double sub = value2 - value1;
                    return sub >= 0 ? -1 : 1;
                }
            });
            Iterator<Map.Entry<Integer, Double>> iter = entryList.iterator();
            Map.Entry<Integer, Double> tmpEntry = null;
            while (iter.hasNext()) {
                tmpEntry = iter.next();
                sortedMap.put(tmpEntry.getKey(), tmpEntry.getValue());
            }
        }
        return sortedMap;
    }

    private static String result(Map<String, Integer> m) {

        if (m.size() == 1) {

            Object r = m.keySet().toArray()[0];

            return r.toString();
        } else {

            List<Map.Entry<String, Integer>> entryList = new ArrayList<Map.Entry<String, Integer>>(m.entrySet());
            Collections.sort(entryList, new Comparator<Map.Entry<String, Integer>>() {
                public int compare(Entry<String, Integer> entry1, Entry<String, Integer> entry2) {
                    double value1 = 0;
                    double value2 = 0;
                    try {
                        value1 = entry1.getValue();
                        value2 = entry2.getValue();
                    } catch (NumberFormatException e) {
                        value1 = 0;
                        value2 = 0;
                    }

                    double sub = value2 - value1;
                    return sub >= 0 ? 1 : -1;
                }
            });
            Iterator<Map.Entry<String, Integer>> iter = entryList.iterator();

            return iter.next().getKey();

        }

    }

    private static double[][] dim12dim2(double[] in, int dim) {

        double[][] out = new double[dim][in.length];

        for (int i = 0; i < dim; i++) {
            for (int j = 0; j < in.length; j++) {

                out[i][j] = in[j];
            }
        }

        return out;
    }

    /**
     * 
     * @param want2Classified 要分类的特征值
     * @param dataSet 已经知道分类的特征值
     * @param lable 分类
     * @param k 
     * @return want2Classified所属分类
     * 
     * 1. 使用矩阵计算want2Classified与dataSet中每一组特征值的距离
     * 2. 按距离远近增序排序
     * 3. 取最近的k组特征值分类
     * 4. 其中出现频率最高的为want2Classified所属分类
     */
    public static String compute(double[] want2Classified, double[][] dataSet, String[] lable, int k) {

        /*1*/
        Matrix in = DenseMatrix.Factory.importFromArray(KNN.dim12dim2(want2Classified, dataSet.length));

        DenseDoubleMatrix2D y = DenseMatrix.Factory.importFromArray(dataSet);

        // 减法
        Matrix sub = in.minus(y);
        System.out.println(sub);
        // 乘法(乘方)
        sub = sub.times(sub);
        System.out.println(sub);
        // 矩阵中的每一行相加
        sub = sub.sum(Calculation.Ret.NEW, 1, false);
        System.out.println(sub);
        // 开方
        sub = sub.sqrt(Calculation.Ret.NEW);
        System.out.println(sub);

        // 二维数组变为一维数组
        double[][] f = sub.toDoubleArray();

        double[] f1 = new double[f.length];

        for (int i = 0; i < f.length; i++) {
            f1[i] = f[i][0];
        }

        LinkedHashMap<Integer, Double> l = new LinkedHashMap<Integer, Double>();

        for (int i = 0; i < f1.length; i++) {

            l.put(i, f1[i]);
        }

        /*2*/
        LinkedHashMap<Integer, Double> sortedM = KNN.sortMapByValue(l);

        Object[] set = sortedM.keySet().toArray();

        /*3*/
        HashMap<String, Integer> m = new HashMap<String, Integer>();

        for (int i = 0; i < k; i++) {

            int index = (Integer) set[i];
            if (m.containsKey(lable[index])) {
                m.put(lable[index], m.get(lable[index]) + 1);
            } else {
                m.put(lable[index], 1);
            }

        }

        /*4*/
        return KNN.result(m);
    }

    public static void main(String[] args) {

        double[] sample = { 0.1, 0.2 };
        double[][] features = { { 1, 1.1 }, { 1, 1 }, { 0, 0 }, { 0, 0.1 } };
        String[] target = { "a", "a", "b", "b" };
        int k = 3;

        System.out.println(KNN.compute(sample, features, target, k));

    }

}
